import Message from './message'
import {Device} from 'mediasoup-client';
import Config from '../config'

class Mediasoup {
    constructor(openVideo, openAudio, startCallback, videoCallback, consumeCallback, destroyCallback) {
        this._openVideo = openVideo
        this._openAudio = openAudio

        this._videoCallbck = videoCallback
        this._consumeCallback = consumeCallback
        this._destroyCallback = destroyCallback
        this._startCallback = startCallback
        this._heartbeatTime = 8000; //设置 ping 事件
        this._pongTime = 12000;
        this._width = 320;
        this._height = 180;
        this._fps = 15;
        this._roomId = Config.roomId
        this._videoTrack = undefined;
        this._audioTrack = undefined;
        this._videoStream = '';
        this._videoProduce = undefined;
        this._audioProduce = undefined;
        this.isCreateTransport = false
        this._consumerMap = new Map() //存放所有消费信息
        this.isCanVideo = false
        this.isCanAudio = false
        // this.websocket = new WebSocket("wss://www.duxingzhe.top:4443/roomId/" + this._roomId);
        this.connect()
    }

    connect(){
        this.websocket = new WebSocket(Config.url + this._roomId);

        this._peerId = ''
        this.initMemberInfo(this._peerId)
        this.websocket.addEventListener('message', ev => {
            this.message(ev)
        })
        this.websocket.addEventListener('open', () => {
            this.open()
            this.ping()
        })
    }

    join() {
        this.send(Message.msgType.join, {})
    }

    /**
     * 太久时间没有收到服务器回复，关闭重新链接
     */
    pong() {
        if (this._pongInterval !== undefined) clearTimeout(this._pongInterval)
        this._pongInterval = setTimeout(() => {
            this.websocket.close()
            this.connect()
        }, this._pongTime)
    }

    ping(){
        if (this._heartbeatInterval !== undefined) clearTimeout(this._heartbeatInterval)
        this._heartbeatInterval = setTimeout(() => {
            this.send(Message.msgType.ping, {})
        }, this._heartbeatTime)
    }

    open() {
        //   this.join()
    }

    send(type, data) {
        this.websocket.send(Message.encoder(type, {
            ...data,
            ...this.memberInfo
        }))
    }

    //初始话公共信息
    initMemberInfo(peerId) {
        this._peerId = peerId
        this.memberInfo = {
            peerId: peerId,
            roomId: this._roomId
        }
    }

    //1、初始化设备
    async initDevice(routerRtpCapabilities) {
        this.device = new Device()
        await this.device.load({
            routerRtpCapabilities
        })

        if (this.device.canProduce("video")) {
            this.isCanVideo = true
        }
        if (this.device.canProduce("audio")) {
            this.isCanAudio = true
        }
        this.isCreateTransport = true
        this._startCallback(this._peerId)
        console.log(this)
    }

    // 2、创建传输通道
    async createTransport(isConsumer) {
        if (this.isCreateTransport)
            this.send(Message.msgType.createTransport, {
                isConsumer: isConsumer
            })
        else
            console.log('createTransport error')
    }


    // 3、创建视频推送端
    async createSendTransport(data) {
        console.log('video', this._video)
        const {
            id,
            iceParameters,
            iceCandidates,
            dtlsParameters,
            sctpParameters
        } = data;
        this.sendTransport = this.device.createSendTransport(data);

        this.sendTransport.on('connect', ({dtlsParameters}, callback, errback) => {
            this.createSendTransportConnect(dtlsParameters, callback, errback)
        })

        this.sendTransport.on("produce", async ({kind, rtpParameters, appData}, callback, errback) => {
            console.log('fm', rtpParameters)
            await this.produce({kind, rtpParameters, appData}, callback, errback)
        })
        if (this._openVideo)
            await this.openVideo();
        if (this._openAudio)
            await this.openAudio();
        this.callVideoView()

    }

    callVideoView() {
        if (this._videoTrack === undefined && this._audioTrack === undefined) return
        if (this._videoTrack === undefined) {
            this._videoCallbck(new MediaStream([this._audioTrack]))
        } else if (this._audioTrack === undefined) {
            this._videoCallbck(new MediaStream([this._videoTrack]))
        } else {
            this._videoCallbck(new MediaStream([this._videoTrack, this._audioTrack]))
        }
    }

    async openVideo() {
        // this._videoStream = await navigator.mediaDevices.getUserMedia({
        //     video: true
        // });
        this._videoStream = await navigator.mediaDevices.getUserMedia({
            video: {
                frameRate: {ideal: this._fps}, // 设置合适的帧率
                width: {ideal: this._width}, // 设置合适的宽度
                height: {ideal: this._height}, // 设置合适的高度
            }
        });
        this._videoTrack = this._videoStream.getVideoTracks()[0];
        this._videoProduce = await this.sendTransport.produce({track: this._videoTrack});
    }

    async pauseVideo() {
        this._videoTrack.stop()
        this._videoTrack = undefined
        console.log(this._videoProduce)
        this.send(Message.msgType.pauseProducer, {
            producerId: this._videoProduce.id
        })
    }

    async resumeVideo() {
        if (this._videoProduce === undefined) {
            await this.openVideo()
        } else {
            // const stream = await navigator.mediaDevices.getUserMedia({video: true});
            const stream = await navigator.mediaDevices.getUserMedia({
                video: {
                    frameRate: {ideal: this._fps}, // 设置合适的帧率
                    width: {ideal: this._width}, // 设置合适的宽度
                    height: {ideal: this._height}, // 设置合适的高度
                }
            });
            this._videoTrack = stream.getVideoTracks()[0];

            await this._videoProduce.replaceTrack({track: this._videoTrack});
            this.send(Message.msgType.resumeProducer, {
                producerId: this._videoProduce.id
            })
        }

        this.callVideoView()
    }

    async openAudio() {
        const stream = await navigator.mediaDevices.getUserMedia({
            audio: true
        });
        this._audioTrack = stream.getAudioTracks()[0]
        this._audioProduce = await this.sendTransport.produce({track: this._audioTrack})
    }


    async pauseAudio() {
        this._audioTrack.stop()
        this._audioTrack = undefined
        this.send(Message.msgType.pauseProducer, {
            producerId: this._audioProduce.id
        })
    }

    async resumeAudio() {
        if (this._audioProduce === undefined) {
            await this.openAudio()
        } else {
            const stream = await navigator.mediaDevices.getUserMedia({audio: true});
            this._audioTrack = stream.getAudioTracks()[0];

            await this._audioProduce.replaceTrack({track: this._audioTrack});
            this.send(Message.msgType.resumeProducer, {
                producerId: this._audioProduce.id
            })
        }
        this.callVideoView()
    }

    async reduceVideoBitrate(){
        if (this._openVideo) {

            let update = false;
            // if (this._fps > 2){
            //     this._fps /= 2
            //     update = true
            // }

            if (this._width > 40) {
                update = true
                this._width /= 2
                this._height /= 2
            }


            if (update) {
                console.log("fps %s width %s height %s", this._fps, this._width, this._height)
                this._videoTrack.stop()
                const stream = await navigator.mediaDevices.getUserMedia({
                    video: {
                        frameRate: {ideal: this._fps}, // 设置合适的帧率
                        width: {ideal: this._width}, // 设置合适的宽度
                        height: {ideal: this._height}, // 设置合适的高度
                    }
                });
                this._videoTrack = stream.getVideoTracks()[0];

                await this._videoProduce.replaceTrack({track: this._videoTrack});
                this.callVideoView()
            }

        }

    }

    async createRecvTransport(data) {
        const {
            id,
            iceParameters,
            iceCandidates,
            dtlsParameters,
            sctpParameters
        } = data;

        this.recvTransport = await this.device.createRecvTransport({
            id,
            iceParameters,
            iceCandidates,
            dtlsParameters: {
                ...dtlsParameters,
                role: 'auto'
            },
            sctpParameters
        });

        this.recvTransport.on('connect', ({dtlsParameters}, callback, errback) => {
            this.createRecvTransportConnect(dtlsParameters, callback, errback)
        })

    }

    // 生产transport
    async createSendTransportConnect(dtlsParameters, callback, errback) {
        console.log(dtlsParameters)
        this._sendTransportCallback = callback;
        this.send(Message.msgType.connectWebRtcTransport, {
            transportId: this.sendTransport.id,
            dtlsParameters,
            isConsumer: false
        })
    }

    // 接收transport
    async createRecvTransportConnect(dtlsParameters, callback, errback) {
        this._recvTransportCallback = callback;
        this.send(Message.msgType.connectWebRtcTransport, {
            transportId: this.recvTransport.id,
            dtlsParameters,
            isConsumer: true
        })
    }

    async statistics(data){
        if ( data.rtpSendBitrate > data.availableOutgoingBitrate) {
            console.log("服务器带宽不足")
            await this.reduceVideoBitrate()
        }
    }

    //生产者
    async produce({kind, rtpParameters, appData}, callback, errback) {
        this._produceCallbck = callback
        this.send(Message.msgType.producer, {
            transportId: this.sendTransport.id,
            kind,
            rtpParameters,
            appData
        })
    }


    //消息处理
    async message(event) {
        let data = (event.data)
        const decoder = Message.decoder(data)
        this.pong()
        switch (decoder.type) {
            case Message.msgType.authorization:
                this.initMemberInfo(decoder.data.peerId)
                this.join()
                break;
            case Message.msgType.join:
                this.initMemberInfo(decoder.data.peerId)
                // 创建mediasoup的RtpCapabilities对象
                await this.initDevice(decoder.data.rtpCapabilities)
                break;
            case Message.msgType.createTransport:

                // const iceCandidates = decoder.data.iceCandidates[0];
                // iceCandidates.ip = "47.113.200.22"
                // decoder.data.iceCandidates[0] = iceCandidates
                // console.error(decoder.data)
                if (decoder.data.isConsumer) {
                    await this.createRecvTransport(decoder.data)
                } else {
                    await this.createSendTransport(decoder.data)
                }
                break;
            case Message.msgType.connectWebRtcTransport:
                if (decoder.data.isConsumer) {
                    this._recvTransportCallback()
                } else {
                    console.log(this._sendTransportCallback)
                    this._sendTransportCallback()
                }

                break;
            case Message.msgType.producer:
                this._produceCallbck({
                    id: decoder.data.producerId
                })
                break

            case Message.msgType.newConsumer: {
                console.log('recvtransport', this.recvTransport)
                const producerId = decoder.data.producerId
                const consumer = await this.recvTransport.consume(
                    {
                        id: decoder.data.id,
                        producerId: producerId,
                        kind: decoder.data.kind,
                        rtpParameters: decoder.data.rtpParameters
                    });
                const peerId = decoder.data.peerId
                if (this._consumerMap.get(peerId) === undefined) {
                    this._consumerMap.set(peerId, new Map())
                }
                this._consumerMap.get(peerId).set(producerId, consumer)
                // Render the remote video track into a HTML video element.
                this._consumeCallback(consumer, decoder.data.peerId, decoder.data.pause)

                console.log('recv end')
            }
                break
            case Message.msgType.memberDestroy:
                this._destroyCallback(decoder.data)
                break

            case Message.msgType.pauseConsumer: {
                const {peerId, producerId} = decoder.data
                const consumer = this._consumerMap.get(peerId).get(producerId)
                this._consumeCallback(consumer, decoder.data.peerId, true)
            }
                break
            case Message.msgType.resumeConsumer: {
                const {peerId, producerId} = decoder.data
                const consumer = this._consumerMap.get(peerId).get(producerId)
                this._consumeCallback(consumer, decoder.data.peerId, false)
                break
            }
            case Message.msgType.pong:{
                this.ping()
                break
            }
            case Message.msgType.statistics: {
                this.statistics(decoder.data)
                break
            }

        }
    }


}

export default Mediasoup
